﻿/** @class FieldGraphGenerator
 *  @brief Genetate a field graph, whcih contains a scalar field with a
 *  scalar bar, and a vector field. This class support change different field
 *  from the given polyDatas.
 */

#ifndef PST_FIELDGRAPHGENERATOR_H
#define PST_FIELDGRAPHGENERATOR_H

#include <QList>
#include <array>
#include <QObject>
#include <QDebug>
#include <vtkSmartPointer.h>

class vtkScalarBarActor;
class vtkActor;
class vtkLODActor;
class vtkGlyph3D;
class vtkLookupTable;
class vtkPolyDataMapper;
class vtkDataSetMapper;
class vtkRenderer;
class vtkArrowSource;
class vtkRenderWindowInteractor;
class vtkScalarBarWidget;
class vtkMaskPoints;
class vtkDoubleArray;
class vtkDataSet;
class vtkDataArray;
class vtkPointSet;

namespace pst
{
    class PipelineManager;

    class FieldGraphGenerator : public QObject
    {
        Q_OBJECT
    public:
        explicit FieldGraphGenerator(QObject* parent = nullptr);
        ~FieldGraphGenerator();

    public:
        enum class FieldType
        {
            SCALARS,
            VECTORS,
        };

        enum class StructureType
        {
            POINTS,
            CELLS
        };

        struct ArrayInformation
        {
            int arrayIndex = -1;
            QString arrayName = "";
            int arrayComponent = -1;
            StructureType type = StructureType::POINTS;

            void printArrayInformation() const
            {
                qDebug() << "arrayIndex = " << arrayIndex;
                qDebug() << "arrayName = " << arrayName;
                qDebug() << "arrayComponent = " << arrayComponent;
                //QString typeStr = (type == StructureType::POINTS ? "POINTS" : "CELLS");
                qDebug() << "type = " << (type == StructureType::POINTS ? "POINTS" : "CELLS");
            }

            bool operator==(const ArrayInformation& other)
            {
                // 根据实际需求定义相等的条件，这里比较两个成员变量
                return this->arrayIndex == other.arrayIndex && this->arrayName == other.arrayName && this->arrayComponent == other.arrayComponent && this->type == other.type;
            }
        };

    public:
        //设置polyData，用于显示
        //包含几何和拓扑结构，同时包括场数据
        void SetInputDataSet(vtkDataSet* data);

        void PrintInputDataSetInformation();
        //获取polyData，可用于重新为其设置属性
        vtkDataSet* GetInputData();

        //应该在没有设置标量场时获取，并且只获取一次。因为设置标量场可能会增加属性。
        //获取属性表points单元下标及其对应的数据名称
        QList<ArrayInformation> GetPointsArrayIndexAndNames();
        //获取属性表cells单元下标及其对应的数据名称
        QList<ArrayInformation> GetCellsArrayIndexAndNames();

        //获取激活的场的原始映射范围
        std::array<double, 2> GetActiveFieldOriginalRange();

        /**
         * 标量场控制方法
         */

         //使用数组下标来设置作为标量属性的数组下标
         //不进行下标范围检查，由用户保证
        bool SetActiveScalarAttribute(int index, const StructureType type, 
            const int compIndex = -1, bool isLog = false, int logFactor = 10, bool isNormalized = false);

        PipelineManager* GetScalarPipelineManager();

        void UpdateScalarFieldActor();  //更新标量场actor

        //获取标量场actor(含颜色)，必须设置标量场属性才能获取
        vtkActor* GetScalarsFieldActor();

        /**
         * 纯几何actor
         */
         //设置颜色
        void SetGeometryActorColor(const double r, const double g, const double b);
        //更新几何actor
        void UpdateGeometryActor();
        vtkActor* GetGeometryActor();

        QString getErrorMessage() const;

        //获取使用的数据，取对数和归一化之后，裁剪之前的数据
        vtkSmartPointer<vtkDataSet> getUsingData() const;  
    private:
        //结合actor,配合矢量场使用
        void InitGeometryActor();
        void PrintArrayInformation(vtkDataArray* array);

        //提取每个属性的单个分量，最初设计目的是 用于将含有多个成分的属性（如三个组分的数据）设置为标量场属性时，
        //提取其中一个单独的组分作为显示。component为-1时，返回magnitude。
        bool GetSingleComponent(vtkDataArray* array, const int component, vtkDataArray* returnArray);

        bool logTheArray(vtkDataArray* array, int logFactory);
        void normalizeTheArray(vtkDataArray* array);
    private:

        bool m_isScalarAttributeSet;
        vtkDataSet* m_inputData;
        vtkSmartPointer<vtkDataSet> m_usingData;

        vtkActor* m_geometryActor;
        vtkActor* m_scalarFieldActor;
        vtkDataSetMapper* m_geometryMapper;      //几何mapper
        vtkDataSetMapper* m_scalarsFieldMapper;   //标量场mapper
        StructureType m_currentUsingScalarType;   //表明当前使用的标量属性属于POINTS的还是CELLS
        int m_currentAddedPointArrayIndex; //设置标量场属性，当多组分array的一个分类被添加后，下次设置时，用于移除。
        int m_currentAddedCellArrayIndex; //设置标量场属性，当多组分array的一个分类被添加后，下次设置时，用于移除。
        std::array<double, 2> m_currentScalarsArrayRange; //当前设置的标量场数值范围
        PipelineManager* m_scalarPipelineManager; //标量场管道管理器
        QString m_errorMsg;
    };


} //namespace pst
#endif // PST_FIELDGRAPHGENERATOR_H
